package com.googlecode.phpunit4eclipse.j2phpunit.parser;


/**
 * @author Bertrand Paquet
 */
public class PHPSerializedParser {

	private static String removeQuote(String s) throws PHPParseException {
		if (!s.startsWith("\"") || !s.endsWith("\"")) {
			throw new PHPParseException("Unable to remove quote from " + s);
		}
		return s.substring(1, s.length() - 1);
	}
	
	static class PHPParseResult {
		public Object phpElement;
		public String leaving;
		
		public PHPParseResult(Object phpElement, String leaving) {
			this.phpElement = phpElement;
			this.leaving = leaving;
		}
		
	}
	
	public static Object parse(String s) throws PHPParseException {
		PHPParseResult result = internalParse(s);
		if (result.leaving.length() != 0) {
			throw new PHPParseException("Not parsed string " + result.leaving);
		}
		return result.phpElement;
	}
	
	static PHPParseResult internalParse(String s) throws PHPParseException {
		if (s.charAt(0) == 'i' || s.charAt(0) == 'R' || s.charAt(0) == 'r') {
			int index = 2;
			while((s.charAt(index) >= '0' && s.charAt(index) <= '9') || s.charAt(index) == '-') {
				index ++;
			}
			String i = s.substring(2, index);
			return new PHPParseResult(Integer.parseInt(i), s.substring(index));
		}
		else if (s.charAt(0) == 'd') {
			int index = 2;
			while((s.charAt(index) >= '0' && s.charAt(index) <= '9') || s.charAt(index) == '-' || s.charAt(index) == '.') {
				index ++;
			}
			String i = s.substring(2, index);
			return new PHPParseResult(Double.parseDouble(i), s.substring(index));
		}
		else if (s.charAt(0) == 'b') {
			if (s.charAt(2) == '0') {
				return new PHPParseResult(false, s.substring(3));
			}
			else if (s.charAt(2) == '1') {
				return new PHPParseResult(true, s.substring(3));
			}
			else {
				throw new PHPParseException("Unable to parse boolean " + s);
			}
		}
		else if (s.charAt(0) == 's') {
			String [] l = s.split(":");
			int size = Integer.parseInt(l[1]);
			int start = s.indexOf(":", 2);
			String content = s.substring(start + 1);
			content = content.substring(0, size + 2);
			return new PHPParseResult(removeQuote(content), s.substring(start + 1 + size + 2));
		}
		else if (s.charAt(0) == 'N') {
			return new PHPParseResult(null, s.substring(1));
		}
		else if (s.charAt(0) == 'a') {
			PHPMap array = new PHPMap();
			return parseArray(s, array);
		}
		else if (s.charAt(0) == 'O') {
			String [] l = s.split(":");
			String className = removeQuote(l[2]);
			PHPObject object = new PHPObject(className);
			return parseArray(s, object);
		}
		else {
			throw new PHPParseException("Unable to parse " + s);
		}	
	}
	
	private static PHPParseResult parseArray(String s, PHPMap array) throws PHPParseException {
		int start = s.indexOf("{");
		String out = s.substring(start + 1);
		if (out.startsWith("}")) {
			return new PHPParseResult(array, out.substring(1));
		}
		while(true) {
			PHPParseResult r1 = internalParse(out);
			if (r1.phpElement != null && r1.phpElement instanceof Integer) {
				String index = Integer.toString((Integer) r1.phpElement);
				r1.phpElement = index;
			}
			if (!r1.leaving.startsWith(";") || !(r1.phpElement instanceof String)) {
				throw new PHPParseException("Unable to parse " + out);
			}
			String key = (String) r1.phpElement;
			PHPParseResult r2 = internalParse(r1.leaving.substring(1));
			array.put(key, r2.phpElement);
			out = r2.leaving;
			if (out.startsWith(";")) {
				out = out.substring(1);
			}
			if (out.startsWith("}")) {
				return new PHPParseResult(array, out.substring(1));
			}
		}
	}

}
